#ifndef SOUNDTRACK_H
#define SOUNDTRACK_H

#include <QAbstractAnimation>
#include <Phonon/Phonon>

#include "global.h"
#include "actiontrack.h"

class RobotAction;

/**
 * @brief Provides audio playback for a robot action.
 *
 * The SoundTrack class uses Qt's Phonon multimedia framework to provide
 * audio playback and editing of multiple sound clips in one timeline.  It
 * has pretty good editing capabilities: you can clip/trim sounds,
 * stack them on top of each other, delete, move, and create them.
 *
 * Sounds in the sound track are referenced by their timestamp, which is the
 * point in the timeline when play of the sound starts.  Note that the
 * timestamp can be changed by the trim function; if the beginning of a sound
 * is trimmed, it doesn't start playback until a later time.  It is important
 * to listen to the soundMoved function to be notified when a change happens
 * and update any referential timestamps to the sound.
 *
 * When querying for information about a sound, it returns several pieces of
 * information about both the clip as it is the sound track, and the media
 * underneath (see the SoundInfo struct in global.h).  The timestamp is
 * the position of the sound in the sound track.  The duration is the duration
 * of the sound in the sound track.  The mediaDuration is the overall duration
 * of the sound file, and trimStart is the point within the media that
 * playback begins.  From these four pieces of information, you are able to
 * calculate everything known about the sound.  For example, if you have a
 * sound that has been trimmed to remove the first 3 seconds of the file, and
 * it starts playback at 5 seconds in the timeline you can calculate where the
 * actual media starts by sound.timestamp - sound.trimStart = 5000 - 3000 = 2000 = 2 secs.
 */
class SoundTrack : public QAbstractAnimation, public RobotActionTrack{
    Q_OBJECT

private: 
    /**
     * @brief The internal data structure used by the sound track
     * to represent a sound clip.
     *
     */
    typedef struct soundObject{
        int timestamp;
        int duration;
        int mediaDuration;
        int trimStart;
        Phonon::MediaObject* mediaObject;
        Phonon::AudioOutput* audioOutput;
    } SoundObject;

    int _duration;
    int _prevTime;
    QVector<SoundObject> _sounds;

    void clipSound(SoundObject& sound, int msecFromEnd);
    void updateDuration();


protected:
    /**
     * @brief Re-implemented from QAbstractAnimation to perform playback.
     *
     * @param msec
     */
    void updateCurrentTime(int msec);

    /**
     * @brief Re-implemented from QAbstractAnimation to help with playback control.
     *
     * @param newState
     * @param oldState
     */
    void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState);

    /**
     * @brief Re-implemented from QAbstractAnimation to return the duration of the soundtrack.
     *
     */
    int duration() const;


public:
    /**
     * @brief Constructor.  Creates a new SoundTrack.
     *
     * @param parent
     */
    explicit SoundTrack(QObject *parent = 0);
    ~SoundTrack();

    //Functions for manipulating individual sounds
    /**
     * @brief Sets a sound to play at a given time.
     *
     * If a sound exists at the given timestamp, it is replaced.
     *
     * @param msec The timestamp, in milliseconds, where the sound
     * should begin playing.
     * @param filename The name of the sound file to play.
     */
    void setSoundAt(int msec, QString filename);

    /**
     * @brief Deletes a sound from the sound track.
     *
     * Returns information about the sound deleted.
     *
     * @param msec The timestamp, in milliseconds, of the sound to be deleted.
     */
    SoundInfo deleteSound(int msec);

    /**
     * @brief Moves a sound clip from one time to another.
     *
     * @param msec The timestamp, in milliseconds, of the sound to be moved.
     * @param newMsec The new timestamp to which the sound should be moved.
     */
    void moveSound(int msec, int newMsec);

    /**
     * @brief Clips a sound to a new duration.
     *
     * The duration cannot be longer than there is available media. For example, you
     * cannot clip a sound file with duration 10 seconds to be 20 seconds long.
     * Also, you cannot clip to smaller than 10 milliseconds.
     *
     * @param msec The timestamp, in milliseconds, of the sound to be clipped.
     * @param newDuration The new duration, in milliseconds, of the sound.
     */
    void clipSound(int msec, int newDuration);

    /**
     * @brief Trims playback of a sound from the front and/or back.
     *
     * Sets the playback beginning and end from within the media of a sound. Either
     * trimming direction cannot trim the sound to less than 10 milliseconds in
     * duration.  Note that trimming from the front of a sound will change the
     * the timestamp of that sound.  For example, trimSound(4000,500,0) will change
     * the timestamp to 4500.
     *
     * @param msec The timestamp, in milliseconds, of the sound to be trimmed.
     * @param frontMsec The amount of milliseconds to trim from the front of the sound.
     * @param backMsec The amount of milliseconds to trim from the back of the sound.
     * (THIS IS DIFFERENT FROM clipSound()).
     */
    int trimSound(int msec, int frontMsec, int backMsec);

    //Functions for manipulating the whole sound track
    /**
     * @brief Clips the sound track to a duration.
     *
     * All sounds that end playback past the new duration are clipped to
     * end at the new sound track duration.
     *
     * @param newDuration  The new duration for the sound track.
     */
    void clipTrack(int newDuration);

    /**
     * @brief Moves all of the sound clips in the sound track by the given amount.
     *
     * @param msec The amount, in milliseconds, to move the sounds.
     */
    void moveAll(int msec);

    //Functions for playback
    /**
     * @brief Seeks playback msec to/from the current playback position.
     *
     * @param msec The number of milliseconds to skip.
     */
    void skip(int msec);

    //Functions for information about the soundtrack
    /**
     * @brief Returns information about a sound at the given timestamp, or
     * an invalid SoundInfo struct.
     *
     * @param msec The timestamp of the sound.
     */
    SoundInfo soundAt(int msec) const;

    /**
     * @brief Returns information about all sound clips in the sound track
     *
     */
    QList<SoundInfo> soundInformation() const;

    //Friend functions for serialization
    friend QTextStream &operator<<(QTextStream &out, const RobotAction &action);
    friend QDataStream &operator<<(QDataStream &out, const RobotAction &action);


signals:
    /**
     * @brief Emitted whenever playback begins for a sound.
     *
     * @param msec The timestamp of the sound that has started playback.
     * @param filename The filename of the sound.
     */
    void soundPlayed(int msec, QString filename);

    /**
     * @brief Emitted whenever the playback of a sound has finished.  Right now,
     * this signal is unreliable.
     *
     * @param msec The timestamp of the sound that has finished playback.
     * @param filename  The filename of the sound.
     */
    void soundFinished(int msec, QString filename);

    /**
     * @brief Emitted when the duration of the sound track has changed.
     *
     * @param newDuration The new duration of the sound track.
     * @param oldDuration The previous duration of the sound track.
     */
    void trackDurationChanged(int newDuration, int oldDuration);

    /**
     * @brief Emitted when the duration of a sound clip has changed.
     *
     * @param msec The timestamp of the sound clip.
     * @param newDuration The new duration of the sound clip.
     */
    void soundDurationChanged(int msec, int newDuration);

    /**
     * @brief Emitted when a sound clip has been added/created to the sound track.
     *
     * @param msec The timestamp of the new clip.
     * @param name The filename of the new clip.
     */
    void soundCreated(int msec, QString name);

    /**
     * @brief Emitted when a sound clip has been set, either by creation or changing
     * the media file.
     *
     * @param msec The timestamp of the sound clip.
     * @param name The filename of the sound clip.
     */
    void soundSet(int msec, QString name);

    /**
     * @brief Emitted when a sound clip has been deleted from the sound track.
     *
     * @param msec The timestamp of the sound clip before it was deleted.
     */
    void soundDeleted(int msec);

    /**
     * @brief Emitted when a sound clip has been moved in the timeline
     *
     * @param msec The previous timestamp of the sound clip.
     * @param newMsec The new timestamp of the sound clip.
     */
    void soundMoved(int msec, int newMsec);


private slots:
    void onPreFinishMark(qint32 msecToEnd);
    void onTotalTimeChanged(qint64 newTotalTime);
    void onFinished();
    void onStateChanged(Phonon::State newState, Phonon::State oldState);

    void onDataReady(const QMap<Phonon::AudioDataOutput::Channel,QVector<qint16> > & data);
};

#endif // SOUNDTRACK_H
